package voldp

import (
	"errors"
	"reflect"
)

// Converts a V func to a func that can be called in templates.
// func(args..., perr *error)ret ==> func(args...) (ret, error), if args contain at lest 1 argument.
// If f does not match the prototype above, f itself is returned.
func ConvertVFunc(f any) (result any) {
	t := reflect.TypeOf(f)
	nNumOut := t.NumOut()
	// f must be a function with no more than 1 return value(v function).
	if t.Kind() != reflect.Func || nNumOut > 1 {
		panic(errors.New("错误的方法类型"))
	}
	nNumIn := t.NumIn()
	if nNumIn < 1 || // No argument
		(t.NumIn() == 1 && nNumOut == 0) || // Only one argument and no return value.
		t.In(nNumIn-1) != reflect.TypeOf((*error)(nil)) { // The last argument is not of type *error
		return f
	}
	// Convert it to a function returning (r..., error).
	var in []reflect.Type
	for i := range nNumIn - 1 {
		in = append(in, t.In(i))
	}
	var out []reflect.Type
	for i := range nNumOut {
		out = append(out, t.Out(i))
	}
	out = append(out, reflect.TypeOf((*error)(nil)).Elem())
	rt := reflect.FuncOf(in, out, false)
	result = reflect.MakeFunc(rt, func(args []reflect.Value) (results []reflect.Value) {
		perr := new(error)
		args = append(args, reflect.ValueOf(perr))
		results = reflect.ValueOf(f).Call(args)
		var retErr reflect.Value
		if *perr == nil {
			// reflect.ValueOf(a) returns a zero value of reflect.Value(invalid) if a is nil.
			retErr = reflect.Zero(reflect.TypeOf((*error)(nil)).Elem())
		} else {
			retErr = reflect.ValueOf(*perr)
		}
		results = append(results, retErr)
		return
	}).Interface()
	return
}
